package com.sencorsta.ids.core.data.redis;

import java.util.List;
import java.util.Map;
import java.util.Set;

import com.sencorsta.ids.core.configure.SysConfig;
import com.sencorsta.ids.core.log.Out;
import com.sencorsta.utils.string.StringUtil;
import redis.clients.jedis.GeoCoordinate;
import redis.clients.jedis.GeoRadiusResponse;
import redis.clients.jedis.GeoUnit;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.params.GeoRadiusParam;
import redis.clients.jedis.util.SafeEncoder;

/**
 * 功能描述：Redis中间件缓存基础服务<br>
 * （依赖jedis和commons-pool）
 *
 * @author ICe
 */
public class RedisConnect {

	private JedisPool RedisPool;

	private String name;
	private int choseIndex;

	public RedisConnect(String name,int dbIndex) {
		this.name = name;
		JedisPoolConfig config = new JedisPoolConfig();
		// 在borrow一个jedis实例时，是否提前进行alidate操作；如果为true，则得到的jedis实例均是可用的
		config.setTestOnBorrow(SysConfig.getInstance().getBoolean(name + ".redis.testonborrow", true));
		// 在return给pool时，是否提前进行validate操作
		config.setTestOnReturn(SysConfig.getInstance().getBoolean(name + ".redis.testonreturn", true));
		config.setMaxIdle(SysConfig.getInstance().getInt(name + ".redis.maxidle", 20));
		config.setMaxWaitMillis(SysConfig.getInstance().getInt(name + ".redis.maxwait", 1000));
		config.setMaxTotal(SysConfig.getInstance().getInt(name + ".redis.total", 5));
		String redisHost = SysConfig.getInstance().get(name + ".redis.host", "127.0.0.1");
		int redisPort = SysConfig.getInstance().getInt(name + ".redis.port", 6379);
		String pwd = SysConfig.getInstance().get(name + ".redis.password");
		//int db = SysConfig.getInstance().getInt(name + ".redis.db", 0);
		int timeout = SysConfig.getInstance().getInt(name + ".redis.timeout", 2000);

		RedisPool = new JedisPool(config, redisHost, redisPort, timeout, pwd != null ? pwd.trim() : null, dbIndex);
	}

	public RedisConnect() {
		this.name = "total";
		JedisPoolConfig config = new JedisPoolConfig();
		// 在borrow一个jedis实例时，是否提前进行alidate操作；如果为true，则得到的jedis实例均是可用的
		config.setTestOnBorrow(SysConfig.getInstance().getBoolean(name + ".redis.testonborrow", true));
		// 在return给pool时，是否提前进行validate操作
		config.setTestOnReturn(SysConfig.getInstance().getBoolean(name + ".redis.testonreturn", true));
		config.setMaxIdle(SysConfig.getInstance().getInt(name + ".redis.maxidle", 20));
		config.setMaxWaitMillis(SysConfig.getInstance().getInt(name + ".redis.maxwait", 1000));
		config.setMaxTotal(SysConfig.getInstance().getInt(name + ".redis.total", 5));
		String redisHost = SysConfig.getInstance().get(name + ".redis.host", "127.0.0.1");
		int redisPort = SysConfig.getInstance().getInt(name + ".redis.port", 6379);
		String pwd = SysConfig.getInstance().get(name + ".redis.password");
		//int db = SysConfig.getInstance().getInt(name + ".redis.db", 0);
		int timeout = SysConfig.getInstance().getInt(name + ".redis.timeout", 2000);

		RedisPool = new JedisPool(config, redisHost, redisPort, timeout, pwd != null ? pwd.trim() : null);
	}

	public RedisConnect(int index) {
		this();
		select(index);
	}

	public final Jedis getRedis() {
		Jedis jedis=RedisPool.getResource();
		jedis.select(choseIndex);
		return jedis;
	}

	public final void select(int index) {
		choseIndex=index;
	}

	public final void release(Jedis redis) {
		if (redis != null) {
			redis.close();
		}
	}

	/**
	 * 字符串转字节数组
	 */
	public final byte[] encode(String utf) {
		return SafeEncoder.encode(utf);
	}

	/**
	 * 字节数组转字符串
	 */
	public final String decode(byte[] body) {
		if (body == null || body.length == 0)
			return null;
		return SafeEncoder.encode(body);
	}

	public final String get(String key) {
		Jedis redis = getRedis();
		try {
			return redis.get(key);
		} finally {
			release(redis);
		}
	}

	public final String hget(String key, String field) {
		Jedis redis = getRedis();
		try {
			return redis.hget(key, field);
		} finally {
			release(redis);
		}
	}

	public final JSONObject hgetJson(String key, String field) {
		Jedis redis = getRedis();
		try {
			String tempArr=redis.hget(key, field);
			if (StringUtil.isEmpty(tempArr)) {
				return null;
			}else {
				return JSON.parseObject(tempArr);
			}
		} finally {
			release(redis);
		}
	}


	public final JSONArray hgetArr(String key, String field) {
		Jedis redis = getRedis();
		try {
			String tempArr=redis.hget(key, field);
			if (StringUtil.isEmpty(tempArr)) {
				return new JSONArray();
			}else {
				return JSONArray.parseArray(tempArr);
			}
		} finally {
			release(redis);
		}
	}

	public final List<String> hmget(String key, String... fields) {
		Jedis redis = getRedis();
		try {
			return redis.hmget(key, fields);
		} finally {
			release(redis);
		}
	}

	public final Long hset(String key, String field, String value) {
		Jedis redis = getRedis();
		try {
			return redis.hset(key, field, value);
		} finally {
			release(redis);
		}
	}


	public final String hmset(String key, Map<String, String> map) {
		Jedis redis = getRedis();
		try {
			return redis.hmset(key, map);
		} finally {
			release(redis);
		}
	}

	public final Long hlen(String key) {
		Jedis redis = getRedis();
		try {
			return redis.hlen(key);
		} finally {
			release(redis);
		}
	}

	public final String put(String key, String value) {
		return set(key, value);
	}

	public final String put(String key, String value, int expired) {
		return set(key, value, expired);
	}

	public final String set(String key, String value) {
		Jedis redis = getRedis();
		try {
			return redis.set(key, value);
		} finally {
			release(redis);
		}
	}

	public final String set(String key, String value, int expired) {
		Jedis redis = getRedis();
		try {
			return redis.setex(key, expired, value);
		} finally {
			release(redis);
		}
	}

	public final Long remove(String key) {
		return del(key);
	}

	public final Long del(String key) {
		Jedis redis = getRedis();
		try {
			return redis.del(key);
		} finally {
			release(redis);
		}
	}

	public final Long hremove(String key, String field) {
		return hdel(key, field);
	}

	public final Long hdel(String key, String field) {
		Jedis redis = getRedis();
		try {
			return redis.hdel(key, field);
		} finally {
			release(redis);
		}
	}

	public final boolean exists(String key) {
		Jedis redis = getRedis();
		try {
			return redis.exists(key);
		} finally {
			release(redis);
		}
	}

	public final boolean hexists(String key, String field) {
		Jedis redis = getRedis();
		try {
			return redis.hexists(key, field);
		} finally {
			release(redis);
		}
	}

	public final Set<String> hkeys(String key) {
		Jedis jedis = getRedis();
		try {
			return jedis.hkeys(key);
		} finally {
			release(jedis);
		}
	}

	public final List<String> hvals(String key) {
		Jedis jedis = getRedis();
		try {
			return jedis.hvals(key);
		} finally {
			release(jedis);
		}
	}

	public final Map<String, String> hgetAll(String key) {
		Jedis redis = getRedis();
		try {
			return redis.hgetAll(key);
		} finally {
			release(redis);
		}
	}

	public final Long expire(String key, int sec) {
		Jedis redis = getRedis();
		try {
			return redis.expire(key, sec);
		} finally {
			release(redis);
		}
	}

	public final Long hincr(String key, String field, long value) {
		Out.debug("开始增加值:",field," --> ",value," uid:"+key);
		Jedis redis = getRedis();
		try {
			return redis.hincrBy(key, field, value);
		} finally {
			release(redis);
		}
	}

	public final Double hincr(String key, String field, double value) {
		Jedis redis = getRedis();
		try {
			return redis.hincrByFloat(key, field, value);
		} finally {
			release(redis);
		}
	}

	public final Double incr(String key, double value) {
		Jedis redis = getRedis();
		try {
			return redis.incrByFloat(key, value);
		} finally {
			release(redis);
		}
	}

	public final Long incr(String key, long value) {
		Jedis redis = getRedis();
		try {
			return redis.incrBy(key, value);
		} finally {
			release(redis);
		}
	}

	public final void publish(String channel, byte[] msg) {
		Jedis redis = getRedis();
		try {
			redis.publish(encode(channel), msg);
		} finally {
			release(redis);
		}
	}

	public final short getShort(String key) {
		String value = get(key);
		return StringUtil.isEmpty(value) ? 0 : Short.valueOf(value);
	}

	public final int getInt(String key) {
		String value = get(key);
		return StringUtil.isEmpty(value) ? 0 : Integer.valueOf(value);
	}

	public final long getLong(String key) {
		String value = get(key);
		return StringUtil.isEmpty(value) ? 0 : Long.valueOf(value);
	}

	public final float getFloat(String key) {
		String value = get(key);
		return StringUtil.isEmpty(value) ? 0 : Float.valueOf(value);
	}

	public final double getDouble(String key) {
		String value = get(key);
		return StringUtil.isEmpty(value) ? 0 : Double.valueOf(value);
	}

	public final short hgetShort(String key, String field) {
		String value = hget(key, field);
		return StringUtil.isEmpty(value) ? 0 : Short.valueOf(value);
	}

	public final int hgetInt(String key, String field) {
		String value = hget(key, field);
		return StringUtil.isEmpty(value) ? 0 : Integer.valueOf(value);
	}

	public final long hgetLong(String key, String field) {
		String value = hget(key, field);
		return StringUtil.isEmpty(value) ? 0 : Long.valueOf(value);
	}

	public final float hgetFloat(String key, String field) {
		String value = hget(key, field);
		return StringUtil.isEmpty(value) ? 0 : Float.valueOf(value);
	}

	public final double hgetDouble(String key, String field) {
		String value = hget(key, field);
		return StringUtil.isEmpty(value) ? 0 : Double.valueOf(value);
	}

	public final void setDouble(String key, double value) {
		put(key, String.valueOf(value));
	}

	public final void setLong(String key, long value) {
		put(key, String.valueOf(value));
	}

	public final void hsetDouble(String key, String field, double value) {
		hset(key, field, String.valueOf(value));
	}

	public final void hsetLong(String key, String field, long value) {
		hset(key, field, String.valueOf(value));
	}

	public final Long hreduce(String key, String field, long value) {
		return hincr(key, field, -value);
	}

	public final Double hreduce(String key, String field, double value) {
		return hincr(key, field, -value);
	}

	public final Double reduce(String key, double value) {
		return incr(key, -value);
	}

	public final Long reduce(String key, long value) {
		return incr(key, -value);
	}

	public Set<String> keys(String pattern) {
		Jedis redis = getRedis();
		try {
			return redis.keys(pattern);
		} finally {
			release(redis);
		}
	}

	public final void destroy() {
		if (RedisPool != null) {
			RedisPool.destroy();
			RedisPool = null;
		}
	}

	public String toString() {
		return "GRedis." + this.name;
	}

	/**
	 * 增加地理位置的坐标
	 *
	 * @param key
	 * @param coordinate
	 * @param memberName
	 * @return
	 */
	public Long geoadd(String key, GeoCoordinate coordinate, String memberName) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.geoadd(key, coordinate.getLongitude(), coordinate.getLatitude(), memberName);
		} catch (Exception e) {
			Out.debug("redis geoadd 出错:" + e.getMessage());
		} finally {
			release(jedis);
		}
		return null;
	}

	/**
	 * 批量添加地理位置
	 *
	 * @param key
	 * @param memberCoordinateMap
	 * @return
	 */
	public Long geoadd(String key, Map<String, GeoCoordinate> memberCoordinateMap) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.geoadd(key, memberCoordinateMap);
		} catch (Exception e) {
			Out.debug("redis geoadd 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 根据给定地理位置坐标获取指定范围内的地理位置集合（返回匹配位置的经纬度 + 匹配位置与给定地理位置的距离 + 从近到远排序，）(单位米)
	 *
	 * @param key
	 * @param coordinate
	 * @param radius
	 * @return List<GeoRadiusResponse>
	 */
	public List<GeoRadiusResponse> geoRadius(String key, GeoCoordinate coordinate, double radius, int count) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.georadius(key, coordinate.getLongitude(), coordinate.getLatitude(), radius, GeoUnit.M,
					GeoRadiusParam.geoRadiusParam().withDist().withCoord().sortAscending().count(count));
		} catch (Exception e) {
			Out.debug("redis geoRadius 出错:" + e.getMessage());
			e.printStackTrace();
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 根据给定地理位置获取指定范围内的地理位置集合（返回匹配位置的经纬度 + 匹配位置与给定地理位置的距离 + 从近到远排序，）
	 *
	 * @param key
	 * @param member
	 * @param radius
	 * @return List<GeoRadiusResponse>
	 */
	List<GeoRadiusResponse> georadiusByMember(String key, String member, double radius, int count) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.georadiusByMember(key, member, radius, GeoUnit.M,
					GeoRadiusParam.geoRadiusParam().withDist().withCoord().sortAscending().count(count));
		} catch (Exception e) {
			Out.debug("redis georadiusByMember 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 查询两位置距离
	 *
	 * @param key
	 * @param member1
	 * @param member2
	 * @param unit
	 * @return
	 */
	public Double geoDist(String key, String member1, String member2, GeoUnit unit) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.geodist(key, member1, member2, unit);
		} catch (Exception e) {
			Out.debug("redis geoDist 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 可以获取某个地理位置的geohash值
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public List<String> geohash(String key, String... members) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.geohash(key, members);
		} catch (Exception e) {
			Out.debug("redis geohash 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 获取地理位置的坐标
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public List<GeoCoordinate> geopos(String key, String... members) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.geopos(key, members);
		} catch (Exception e) {
			Out.debug("redis geopos 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 删除地理位置的坐标
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public Long georemove(String key, String... members) {
		return zrem(key,members);
	}

	/**
	 * 增加
	 *
	 * @param key
	 * @param member
	 * @return
	 */
	public Long zadd(String key, double score, String member) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zadd(key, score, member);
		} catch (Exception e) {
			Out.debug("redis zadd 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * List增加
	 *
	 * @param key
	 * @param member
	 * @return
	 */
	public Long lpush(String key,String member) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.lpush(key,member);
		} catch (Exception e) {
			Out.debug("redis lpush 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}
	/**
	 * List取值
	 *
	 */
	public List<String> lrange(String key, long statr, long stop ) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.lrange(key,statr,stop);
		} catch (Exception e) {
			Out.debug("redis lrange 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * List取All
	 *
	 */
	public List<String> lrangeAll(String key ) {
		return lrange(key,0,-1);
	}

	/**
	 * 增加
	 *
	 * @param key
	 * @param scoreMembers
	 * @return
	 */
	public Long zadd(String key, Map<String, Double> scoreMembers) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zadd(key, scoreMembers);
		} catch (Exception e) {
			Out.debug("redis zadd 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 删除
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public Long zrem(String key, String... members) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zrem(key, members);
		} catch (Exception e) {
			Out.debug("redis georemove 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 增加
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public Double zincrby(String key, double score, String members) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zincrby(key, score, members);
		} catch (Exception e) {
			Out.debug("redis georemove 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 查看分数
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public Double zscore(String key, String members) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zscore(key, members);
		} catch (Exception e) {
			Out.debug("redis zscore 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 * 范围访问
	 *
	 * @param key
	 * @param startIndex
	 * @param endIndex
	 * @return
	 */
	public Set<String> zrevrange(String key, long startIndex, long endIndex) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zrevrange(key, startIndex, endIndex);
		} catch (Exception e) {
			Out.debug("redis zrevrange 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 *查看排名 从小到大
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public Long zrank(String key, String members) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zrank(key, members);
		} catch (Exception e) {
			Out.debug("redis zrank 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}

	/**
	 *查看排名 从大到小
	 *
	 * @param key
	 * @param members
	 * @return
	 */
	public Long zrevrank(String key, String members) {
		Jedis jedis = null;
		try {
			jedis = getRedis();
			return jedis.zrevrank(key, members);
		} catch (Exception e) {
			Out.debug("redis zrank 出错:" + e.getMessage());
		} finally {
			if (null != jedis)
				jedis.close();
		}
		return null;
	}
}
